// {a: {},list: []}

import { ArrayMethods } from "./arr";
import { Dep } from "./dep"; //每个属性都对应一个dep ,data中的每个属性 都生成一个对象，其对象里面是 存取多个watcher实例

export function observer(data) {
  console.log(data, "开始数据观察data---》");
  //1. 对象 vue2
  //1.1 判断类型是否存在，不是对象 不需要劫持，是对象的话在进行数据劫持
  if (typeof data != "object" || data == null) {
    return data;
  }
  //通过一个类进行观测
  return new Observer(data);
}

class Observer {
  constructor(data) {
    // 往每次new 出来的Observer上添加当前实列，以供后续可以调用实列中的方法。
    Object.defineProperty(data, "__data__", {
      enumerable: false, //不可枚举
      configurable: true, //
      value: this,
    });
    // 0.1 给每个对象上添加一个dep---包含了 对象{} 和数组【】 因为数组的typeof [] == 'object
    this.dep = new Dep();
    console.log(
      this.dep,
      "dep-observer观察每一个属性1-为每个属性添加也给dep管理者管理watcher",
    );
    //0.数组
    if (Array.isArray(data)) {
      //console.log(data)
      data.__proto__ = ArrayMethods; //放到原型链上,这样每个数组就就可以访问 我们自己重写的新的变异的数组方法，

      //1--如果是数组对象 需要对 对象也进行劫持
      this.observerArrayObj(data);
    } else {
      // 1.所以需要遍历对象,对对象下的，一层每一个属性进行劫持
      this.walk(data);
    }
  }
  walk(data) {
    //{a:'zz',msg:'hekkl'} dat中第一层数据进行劫持了
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      //对对象  下的每一个属性都进行劫持
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value); //第一层
      // //递归很多层数据 属性还是对象等情况---这个地方不建议递归
      // if(typeof value == 'object' && value) {
      //     this.walk(value)
      // }
    }
  }
  observerArrayObj(data) {
    // [{a: 1}] 对data中数组内已经声明的对象进行劫持
    data.map((item) => {
      observer(item);
    });
  }
}

//对 对象中的属性单个进行劫持
function defineReactive(data, key, value) {
  let returnObserverItem = observer(value); //深层代理 也就是递归对象属性已经是对象的---这个才是源码中递归的地方
  let dep = new Dep(); // 给每个属性添加一个dep对象管理者对象
  console.log(
    returnObserverItem,
    dep,
    "returnObserverItemreturnObserverItem--and--new出来的dep",
  ); // 每个对象下有个dep。可以进行调用dep下的收集依赖的方法
  Object.defineProperty(data, key, {
    get() {
      console.log(
        "在触发defineProPerty属性下的值--获取",
        value,
        Dep.target,
        "subScribe-target",
      );
      // 收集依赖---初次渲染之后，当再次或者值，或者再次修改值后，已经可以获取当前watcher，一个数据和视图直接的关系
      if (Dep.target) {
        dep.subScribe();
        // 如果对象下的dep存在---标识可能是数组--or 对象{}
        if (returnObserverItem.dep) {
          returnObserverItem.dep.subScribe(); //数组收集依赖
        }
      }

      return value;
    },
    set(newValue) {
      console.log("修改---data---computed", dep, newValue);
      //1判断修改的值 是否和原来一致 一致不需要后续操作,否则更新value值为最新的值
      if (newValue == value) return value;
      //2如果设置的值 是一个新的对象那么继续进行递归劫持
      observer(newValue); //深层代理 也就是递归---这个才是源码中递归的地方
      value = newValue;
      dep.notify(value);
      console.log(dep, "depdep--修改");
    },
  });
}

//vue2 Object.defineProperty进行劫持 缺点 只能劫持对象中的一个属性{a:1,b:2}

// 总结：壹（1）对象 1.  Object.defineProperty 缺点 只能对对象中的一个属性进行劫持
// 2. 所以需要遍历 {a:1,b:2} 第一层
// 3.  深层次对象 {obj: {a: 12}}  递归 get set中进行递归判断新设置的对象

// 贰   next 数组劫持  {list: [1,2]}  {list: [{a:1}]}
// 方法函数劫持，重写数组方法  arr.push  arr.splice arr.....
